package 狂神多线程;

public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();
        new Thread(station,"苦逼的我").start();
        new Thread(station,"牛逼的你们").start();
        new Thread(station,"可恶的黄牛").start();

    }
}


class BuyTicket implements Runnable{
    private int ticketNums=10;
    boolean flag=true; //外部停止方式
    @Override
    public void run() {
        //买票
    while(true){
        buy();
    }
    }
    //synchronized同步的,锁的是this
    private  void buy(){
        //判断是否有票
        if (ticketNums<=0){
            flag=false;
            return;
        }

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }
}

/**
 * 种负数是什么情况
 * 这种 “票出现负数” 的情况是典型的多线程安全问题，核心原因是多个线程同时对共享变量 ticketNums 进行 “非原子性” 的读写操作，具体分析和解决如下：
 * 一、问题原因
 * 在 buy() 方法中，“判断票是否存在” 和 “减票并打印” 这两个操作不是原子的（即不是 “要么全做，要么全不做”）。
 * 举个例子：当 ticketNums=1 时，三个线程（“苦逼的我”“牛逼的你们”“可恶的黄牛”）可能同时通过了 “if (ticketNums<=0)” 的判断（都认为还有票），然后各自执行 “ticketNums--” 和打印逻辑，最终导致 ticketNums 被减到负数（比如从 1 减到 - 2）。
 * 二、解决方法：用 synchronized 保证操作的 “原子性”
 * 给 buy() 方法添加 synchronized 修饰，让它成为同步方法。这样同一时间只有一个线程能执行 buy() 方法，保证 “判断票是否存在” 和 “减票打印” 的操作是原子的（不会被其他线程打断）。
 * 修改后的 buy() 方法如下：
 * java
 * 运行
 * // 给buy方法添加synchronized修饰，锁的是BuyTicket实例（this）
 * private synchronized void buy(){
 *     // 判断是否有票
 *     if (ticketNums<=0){
 *         flag=false;
 *         return;
 *     }
 *
 *     try {
 *         Thread.sleep(100);
 *     } catch (InterruptedException e) {
 *         throw new RuntimeException(e);
 *     }
 *
 *     // 买票
 *     System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
 * }
 * 三、修改后效果
 * 添加 synchronized 后，三个线程会串行执行 buy() 方法，“判断票是否存在” 和 “减票打印” 的操作会被完整地原子执行，最终票会从 10 依次减到 1，不会出现负数或重复卖票的情况。
 * 简单来说，多线程环境下共享资源的 “读写操作” 必须保证原子性，否则就会出现数据不一致（如负数、重复数据等）。synchronized 是解决这类问题的基础手段之一～
 */
